// This file is part of OpenMeca, an easy software to do mechanical simulation.
//
// Author(s)    :  - Damien ANDRE  <openmeca@gmail.com>
//
// Copyright (C) 2012 Damien ANDRE
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <cassert>
#include <iostream>

#include "OpenMeca/Util/Dimension.hpp"
#include "OpenMeca/Util/Unit.hpp"


namespace OpenMeca
{
  namespace Util
  {

    std::map<std::string, Dimension*> Dimension::all_ = std::map<std::string, Dimension*>();

    std::string Dimension::GetStrType(){return "Dimension";}

    Dimension& 
    Dimension::Get(const std::string str)
    {
      assert(all_.count(str) == 1);
      return *all_[str];
    }
    
    bool 
    Dimension::Exist(const std::string str)
    {
      assert(all_.count(str) < 2);
      return (all_.count(str)==1);
    }



    Dimension::Dimension(std::string name)
      :name_(name),
       units_(),
       siUnit_(0),
       userChoice_(0)
    {
      assert(all_.count(name_)==0);
      all_[name_] = this;
    }
    

    Dimension::~Dimension()
    {
      assert(all_.count(name_)==1);
      all_.erase(all_.find(name_));
    }
    
    void 
    Dimension::AddUnit(Unit& unit)
    {
      assert(units_.count(unit.GetName())==0);
      units_[unit.GetName()] = &unit;
    }

    void 
    Dimension::RemoveUnit(Unit& unit)
    {
      assert(units_.count(unit.GetName())==1);
      units_.erase(units_.find(unit.GetName()));
    }
      
    void 
    Dimension::SetSiUnit(Unit& unit)
    {
      assert(units_.count(unit.GetName())==1);
      siUnit_ = &unit;
      
    }

    
    void 
    Dimension::SetSiUnit()
    {
      //Automactic detection of SiUnits (factor = 1.)
      std::map<std::string, Unit*>::iterator it;
      unsigned int count = 0;

      for ( it=units_.begin() ; it != units_.end(); it++ )
	{
	  if ((*it).second->GetFactor()==1.)
	    {
	      siUnit_ = (*it).second;
	      count ++;
	    }
	}

      assert(count == 1); //The Si unit must be unique !
    }
    
    void 
    Dimension::SetUserChoiceUnit(Unit& unit)
    {
      assert(units_.count(unit.GetName())==1);
      userChoice_ = &unit;
    }


    Unit&  
    Dimension::GetUnit(const std::string& name)
    {
      assert(units_.count(name)==1);
      return *units_[name];
    }
    
    const Unit&  
    Dimension::GetUnit(const std::string& name) const
    {
      assert(units_.count(name)==1);
      return *units_.find(name)->second;
    }
      
    const std::string& 
    Dimension::GetName() const
    {
      return name_;
    }

    const std::map<std::string, Unit*>&
    Dimension::GetUnits() const
    {
      return units_;
    }

    const Unit& 
    Dimension::GetSiUnit() const
    {
      return *siUnit_;
    }

    const Unit& 
    Dimension::GetUserChoice() const
    {
      return *userChoice_;
    }
      
  }
}
